package ex4.gui;
/**
 * 
 * @author Yuvik
 * this class represents the snakes board and acutely all the
 * game logic is implemented here
 */

import javax.swing.*;

import ex4.logic.Configuration;
import ex4.logic.Food;
import ex4.logic.IceCream;
import ex4.logic.Insect;
import ex4.logic.Meat;
import ex4.logic.Snake;
import ex4.logic.Location;

import java.awt.event.*;
import java.awt.*;
import java.util.LinkedList;
import java.util.ListIterator;

public class Board extends JPanel implements ActionListener, KeyListener{

	private static final long serialVersionUID = 1L;
	private int _foodType;
	private ImageIcon[] _food;
	private ImageIcon _snakeHeadIcon;
	private ImageIcon _snakeIcon;
	private int _snakeSpeed;
	private Timer _timer;
	private Timer _fuzzyTimer;
	private Timer _speedTimer;
	private Timer _foodTimer;

	private boolean _fuzzy;

	private int _dx , _dy;
	private int _xFood,_yFood;	
	private LinkedList<Location> _snakeBody;
	private int _snakeSize;
	private int _direction;
	private Snake _snake;
	private Food[] _foodTypes;	
	private JLabel _playerStatus;
	private Game _game;

	public Board(Snake snake, Game game){
		
		this._game=game;

		_playerStatus=new JLabel("playerStatus");
		
		this._snake=snake;
		this._fuzzy=this._snake.get_fuzzy();
		
		_playerStatus.setText(
				"Snake Type: "+ this._snake.get_SnakeTypeName()+
				" score: "+ Integer.toString(this._snake.get_score())+
				" length: "+ Configuration.SNAKESIZE +
				" speed: "+ Configuration.REGULARSPEED+
				" fuzzy: "+ this._snake.get_fuzzy());
		
	
		
		this.add("West", _playerStatus);
	


		//initialize the images
		_food = new ImageIcon[3];
		_food[Configuration.MEAT] = new ImageIcon(getClass().getResource("meat.png"));
		_food[Configuration.INSECT] = new ImageIcon(getClass().getResource("insect.png"));
		_food[Configuration.ICECREAME] = new ImageIcon(getClass().getResource("icecreame.png"));
		_snakeIcon=new ImageIcon(getClass().getResource(snake.get_imageName()));
		_snakeHeadIcon = new ImageIcon(getClass().getResource("snakeHead.png"));

		//initialize the snakes body
		_snakeSize=Configuration.SNAKESIZE;
		initSnakeBody();


		//initialize the direction of the snake
		this._dx=Configuration.DX;
		this._dy = 0;
		this._direction=Configuration.RIGHT;

		//initialize the first food displayed
		generateFood();
		_xFood=400;
		_yFood=300;

		//initialize the food types
		_foodTypes = new Food[3];
		_foodTypes[Configuration.MEAT] = new Meat();
		_foodTypes[Configuration.INSECT] = new Insect();
		_foodTypes[Configuration.ICECREAME] = new IceCream();



		_speedTimer = new Timer(Configuration.SPEEDTIMER,this);
		_fuzzyTimer = new Timer(Configuration.FUZZYTIMER,this);

		
		//initialize the speed of the snake
		_snakeSpeed=Configuration.REGULARSPEED;
		_timer = new Timer(_snakeSpeed,this);
		_timer.start();
		

		
	}

	/**
	 * initialize the location of the snake's body on the board
	 */
	private void initSnakeBody() {
		_snakeBody=new LinkedList<Location>();
		int tX= Configuration.DX;
		int tY= 0;	

		//create the body
		for(int i=0 , max = _snakeSize-1 ; i<max ; i++){
			_snakeBody.addFirst( new Location(tX,tY));
			tX=tX+Configuration.DX;
		}	

		//create the head
		_snakeBody.addFirst( new Location(tX,tY));
	}

	/**
	 * generates Food in a random pattern
	 */
	private void generateFood(){

		_foodType = (int) ( Math.random()*3 ) ;
		_xFood=(int) ( Math.random()* getSize().width );
		_xFood=(_xFood / Configuration.DX ) * Configuration.DX;
		_yFood=(_yFood / Configuration.DY ) * Configuration.DY;

		_foodTimer = new Timer(Configuration.FOODTIMER,this);
		_foodTimer.start();


	}

	/**
	 * the snake eats the food 
	 */
	private void eatFood() {
		_snake.eat(_foodTypes[_foodType]);
		if (_snake.get_speed()!=_snakeSpeed ){
			this._snakeSpeed=_snake.get_speed();
			_speedTimer.start();
		}
		if (_snake.get_fuzzy()){
			this._fuzzy=true;
			_fuzzyTimer.start();
		}
		
		this.updateStatus();
	
		generateFood();
		// TODO Auto-generated method stub

	}

	/**
	 * @param tX
	 * @param tY
	 * @return
	 */
	private boolean notInsideBorderLines(int x, int y) {
		return x >= getSize().width-Configuration.DX | y >= getSize().height | x <0 | y<0;
	}

	/**
	 * @param tX
	 * @param tY
	 * @return
	 */
	private boolean collideItself(int x, int y) {
		ListIterator<Location> itr = _snakeBody.listIterator();
		Location tLoc;

		//go throw the snakes body and check if it doesn't collide itself
		while(itr.hasNext()){	    	
			tLoc=(Location) itr.next();
			if (tLoc.get_x() == x & tLoc.get_y() == y )
				return true;
		}	

		return false;
	}

	/**
	 * @return
	 */
	private int calculateNewHeadYPos() {
		return (_snakeBody.getFirst().get_y() + _dy);
	}

	/**
	 * @return
	 */
	private int calculateNewHeadXPos() {
		return (_snakeBody.getFirst().get_x() + _dx);
	}

	public void endGame() {
		this._game.dispose();
		JFrame frame = new EnterYourNameScreen(this._snake.get_score());	
		frame.setSize(800,600);
		frame.setVisible(true);
		
		if(this._timer.isRunning())
		this._timer.stop();
		
		if(this._fuzzyTimer.isRunning())
		this._fuzzyTimer.stop();
		
		if(this._speedTimer.isRunning())
		this._speedTimer.stop();
		
		
		// TODO Auto-generated method stub

	}


	public void actionPerformed(ActionEvent e){

		if (e.getSource() == _timer){	
			moveSnake();
		}
		if (e.getSource() == _speedTimer){
			this._snakeSpeed = Configuration.REGULARSPEED;
			this._snake.set_speed(this._snakeSpeed);
			_speedTimer.stop();
		}
		if (e.getSource() == _fuzzyTimer){
			this._fuzzy = false;
			this._snake.set_fuzzy(this._fuzzy);
			_fuzzyTimer.stop();
		}
		if (e.getSource() == _foodTimer){
			_foodTimer.stop();
			this.generateFood();

		}

	}

	/**
	 * 
	 */
	private void moveSnake() {
		//calculate the new head position
		int tX = calculateNewHeadXPos() ;
		int tY = calculateNewHeadYPos() ;
		//System.out.println("move Snake to x: "+ tX + " y: " + tY );

		if ( notInsideBorderLines(tX, tY) | collideItself(tX ,tY) ){
			endGame();
			
		}
		else{

			//remove the tail of the snake if needed
			if(_snake.get_length() > _snakeSize )
				_snakeSize++;
			else _snakeBody.removeLast();

			//sets the new head location
			Location tLoc=new Location(tX, tY);
			_snakeBody.addFirst(tLoc);

			//eats food if it can
			if(_xFood == tX & _yFood ==tY){
				eatFood();
			}

			repaint();
		}
	}

	public void paint(Graphics g){
		super.paint(g);
		
		_food[_foodType].paintIcon(this, g, _xFood, _yFood);		
		
		ListIterator<Location> itr = _snakeBody.listIterator();
		Location tLoc;
		//paint the head
		tLoc=(Location) itr.next();
		_snakeHeadIcon.paintIcon(this, g, tLoc.get_x(),tLoc.get_y());

		//paint the rest of the body
		while(itr.hasNext()){	    	
			tLoc=(Location) itr.next();
			_snakeIcon.paintIcon(this, g, tLoc.get_x(),tLoc.get_y());
		}	

	}

	@Override
	public void keyPressed(KeyEvent e) {
		if(_fuzzy)
			FuzzySituation(e);
		else
			notFuzzySituation(e);

	}

	private void FuzzySituation(KeyEvent e) {
		if (e.getKeyCode() == KeyEvent.VK_RIGHT &
				_direction != Configuration.RIGHT){

			_dx = -Configuration.DX;
			_dy = 0;
			_direction=Configuration.LEFT;

		}
		else if (e.getKeyCode() == KeyEvent.VK_LEFT &
				_direction != Configuration.LEFT){
			_dx = Configuration.DX;
			_dy = 0;
			_direction=Configuration.RIGHT;
		}
		else if (e.getKeyCode() == KeyEvent.VK_DOWN &
				_direction != Configuration.DOWN){
			_dx = 0;
			_dy = -Configuration.DY;
			_direction=Configuration.UP;
		}
		else if (e.getKeyCode() == KeyEvent.VK_UP &
				_direction != Configuration.UP){
			_dy = Configuration.DY;
			_dx = 0;
			_direction=Configuration.DOWN;	
		}

	}

	/**
	 * @param e
	 */
	private void notFuzzySituation(KeyEvent e) {
		if (e.getKeyCode() == KeyEvent.VK_LEFT &
				_direction != Configuration.RIGHT){
			_dx = -Configuration.DX;
			_dy = 0;
			_direction=Configuration.LEFT;

		}
		else if (e.getKeyCode() == KeyEvent.VK_RIGHT &
				_direction != Configuration.LEFT){
			_dx = Configuration.DX;
			_dy = 0;
			_direction=Configuration.RIGHT;
		}
		else if (e.getKeyCode() == KeyEvent.VK_UP &
				_direction != Configuration.DOWN){
			_dx = 0;
			_dy = -Configuration.DY;
			_direction=Configuration.UP;
		}
		else if (e.getKeyCode() == KeyEvent.VK_DOWN &
				_direction != Configuration.UP){
			_dy = Configuration.DY;
			_dx = 0;
			_direction=Configuration.DOWN;	
		}
	}
	@Override
	public void keyReleased(KeyEvent arg0) {	
	}
	@Override
	public void keyTyped(KeyEvent arg0) {	
	}
	
	
	private void updateStatus(){
		
		_playerStatus.setText(
				"Snake Type: "+ this._snake.get_SnakeTypeName()+
				" score: " +Integer.toString(this._snake.get_score())+
				" length: "+ Integer.toString(this._snake.get_length())+
				" speed: "+ Integer.toString(this._snake.get_speed())+
				" fuzzy: "+ this._snake.get_fuzzy());
	}
	





}

